#include "tcp_client_port.h"

#ifdef _WINDOWS
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#endif

#include "zmq_manager.h"

TcpClientPort::TcpClientPort(int maxTxFifoSize) : BaseIoPort(maxTxFifoSize)
{
}

TcpClientPort::~TcpClientPort()
{
}

int32_t TcpClientPort::Open(string ip, int32_t port)
{
#ifdef _WINDOWS
    return 0;
#else
    mName = ip;
    mParam = port;
    event_fd_def socFd;

    sockaddr_in remote;
    remote.sin_addr.s_addr = inet_addr(mName.c_str());
    remote.sin_family = AF_INET;
    remote.sin_port = htons((u_short)port);

    bool ret = false;
    int tryTimes = 0;
    while (ret == false && tryTimes++ < 3) {
        socFd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        if (socFd <= 0) {
            LOGE("TcpClient Socket fail");
            return -1;
        }

        /*
        int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
        sockfd：标识一个套接口的描述字。
        level：选项定义的层次；支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
        optname：需设置的选项。
        optval：指针，指向存放选项待设置的新值的缓冲区。
        optlen：optval缓冲区长度。
        */

       //SO_REUSEADDR            许完全重复的捆绑
        int socketoptvalue = 1;
        setsockopt(socFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&socketoptvalue, sizeof(int));

        //SO_RCVBUF              发送缓冲区
        socketoptvalue = 128 * 1024;
        setsockopt(socFd, SOL_SOCKET, SO_RCVBUF, (const char*)(&socketoptvalue), sizeof(int));
        setsockopt(socFd, SOL_SOCKET, SO_SNDBUF, (const char*)&socketoptvalue, sizeof(int));

        socketoptvalue = 3;
        setsockopt(socFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)(&socketoptvalue), sizeof(int));
        setsockopt(socFd, SOL_SOCKET, SO_SNDTIMEO, (const char*)(&socketoptvalue), sizeof(int));

        int error = -1;
        timeval tm;
        fd_set set;
        unsigned long ul = 1;
        ioctl(socFd, FIONBIO, &ul);  //设置为非阻塞模式
        int connectRet = connect(socFd, (struct sockaddr*)&remote, sizeof(remote));
        if (connectRet == 0) {
            LOGI("TcpClient connect success");
            ret = true;
        } else if (connectRet == -1) {
            int selectVal = 0;
            FD_ZERO(&set);
            FD_SET(socFd, &set);
            tm.tv_sec = 20;
            tm.tv_usec = 0;
            selectVal = select(socFd + 1, nullptr, &set, nullptr, &tm);
            if (selectVal > 0) {
                //select success
                socklen_t len = sizeof(error);
                getsockopt(socFd, SOL_SOCKET, SO_ERROR, &error, &len);
                if (error == 0) {
                    //connect success
                    LOGI("TcpClient connect success %s:%d",mName.c_str(),port);
                    ret = true;
                } else {
                    //connect fail
                    ret = false;
                    LOGI("TcpClient connect false, %s, errno:%d", strerror(error), error);
                    break;
                }
            } else {
                //超时或者连接错误，再来
                LOGI("TcpClient connect select err:%d", selectVal);
                CLOSE_SOCKET(socFd);
                continue;
            }
        } else {
            //未知错误
            ret = false;
            LOGI("TcpClient connect err, never arrive");
            break;
        }
        ul = 0;
        ioctl(socFd, FIONBIO, &ul);  //设置为阻塞模式
    }

    if (ret == false) {
        CLOSE_SOCKET(socFd);
        LOGE("TCP connect failed");
        return -2;
    }

    mHandle = (event_fd_def)socFd;
    mIsOpened = true;
    mOpenedClientPortListMutex.lock();
    mOpenedClientPortList.push_back(this);
    mOpenedClientPortListMutex.unlock();
    return socFd;
#endif
}

int32_t TcpClientPort::Close()
{
    if (mIsOpened) {
        mIsOpened = false;
        CLOSE_SOCKET((event_fd_def)mHandle);
        mHandle = 0;
    }
    mOpenedClientPortListMutex.lock();
    mOpenedClientPortList.remove(this);
    mOpenedClientPortListMutex.unlock();
    return 0;
}